<!DOCTYPE html><html><head></head><body><script>

setTimeout(location.reload.bind(location), 700);
document.documentElement.scrollLeft = 0;

var cali = (function() {
    var ac = new AudioContext(),
        fi = ac.createBiquadFilter(),
        freqs = new Float32Array(1),
        mag = new Float32Array(1),
        phase = new Float32Array(1);

    freqs[0] = 14000;
    fi.type = 'peaking';
    fi.frequency.value = 18000;
    fi.Q.value = 0.8;
    fi.gain.value = 1;
    fi.getFrequencyResponse(freqs, mag, phase);

    return mag[0];  // 1.0407 good, 1.0563 bad
})(),
    mp = cali < 1.05;

var can = document.createElement('canvas'),
    cc = can.getContext('2d'),
    w = 2048,
    h = 1024;

w = 2048;

can.width = w;
can.height = h;
document.body.appendChild(can);
can.style.cssText = 'width:' + w + 'px;height:' + h + 'px';

cc.fillStyle = '#000';
cc.fillRect(0, 0, w, h);

var cfg = [ // hz, q, g
    [31.25 * 0.88, 0, 1.4],  // shelf
    [31.25 * 1.04, 0.7, 0.96],  // peak
    [62.5, 0.7, 1],
    [125, 0.8, 1],
    [250, 0.9, 1.03],
    [500, 0.9, 1.1],
    [1000, 0.9, 1.1],
    [2000, 0.9, 1.105],
    [4000, 0.88, 1.05],
    [8000 * 1.006, 0.73, mp ? 1.24 : 1.2],
    //[16000 * 1.00, 0.5, 1.75],  // peak.v1
    //[16000 * 1.19, 0, 1.8]  // shelf.v1
    [16000 * 0.89, 0.7, mp ? 1.26 : 1.2],  // peak
    [16000 * 1.13, 0.82, mp ? 1.09 : 0.75],  // peak
    [16000 * 1.205, 0, mp ? 1.9 : 1.85]  // shelf
];

var freqs = new Float32Array(22000),
    sum = new Float32Array(freqs.length),
    ac = new AudioContext(),
    step = w / freqs.length,
    colors = [
        'rgba(255, 0, 0, 0.7)',
        'rgba(0, 224, 0, 0.7)',
        'rgba(0, 64, 255, 0.7)'
    ];

var order = [];

for (var a = 0; a < cfg.length; a += 2)
    order.push(a);

for (var a = 1; a < cfg.length; a += 2)
    order.push(a);

for (var ia = 0; ia < order.length; ia++) {
    var a = order[ia],
        fi = ac.createBiquadFilter(),
        mag = new Float32Array(freqs.length),
        phase = new Float32Array(freqs.length);

    for (var b = 0; b < freqs.length; b++)
        freqs[b] = b;

    fi.type = a == 0 ? 'lowshelf' : a == cfg.length - 1 ? 'highshelf' : 'peaking';
    fi.frequency.value = cfg[a][0];
    fi.Q.value = cfg[a][1];
    fi.gain.value = 1;

    fi.getFrequencyResponse(freqs, mag, phase);
    cc.fillStyle = colors[a % colors.length];
    for (var b = 0; b < sum.length; b++) {
        mag[b] -= 1;
        sum[b] += mag[b] * cfg[a][2];
        var y = h - (mag[b] * h * 3);
        cc.fillRect(b * step, y, step, h - y);
        cc.fillRect(b * step - 1, y - 1, 3, 3);
    }
}

var min = 999999, max = 0;
for (var a = 0; a < sum.length; a++) {
    min = Math.min(min, sum[a]);
    max = Math.max(max, sum[a]);
}
cc.fillStyle = 'rgba(255,255,255,1)';
for (var a = 0; a < sum.length; a++) {
    var v = (sum[a] - min) / (max - min);
    cc.fillRect(a * step, 0, step, v * h / 2);
}

cc.fillRect(0, 460, w, 1);

</script></body></html>